﻿using NPOI.SS.Formula.Functions;
using NPOI.SS.UserModel;
using NPOI.SS.Util;
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;

namespace EasyCore.Excel
{
    public interface ISheetMapper {
        public bool Written { get; set; }
        public void Write();//写数据到工作簿  
    }

    public abstract class SheetMapper<T>: ISheetMapper
    {
        /// <summary>
        /// Excel映射器
        /// </summary>
        private ExcelMapper ExcelMapper { get; set; }
        
        /// <summary>
        /// Excel操作类
        /// </summary>
        private IWorkbook Workbook { get; set; }

        /// <summary>
        /// 工作簿
        /// </summary>
        protected ISheet Sheet { get; set; }

        /// <summary>
        /// 工作簿名称
        /// </summary>
        public string SheetName { get; set; }

        /// <summary>
        /// 缺省列宽
        /// </summary>
        public int DefaultColumnWidth { get; set; } = 20;

        /// <summary>
        /// 缺省行高
        /// </summary>
        public int DefaultRowHeight { get; set; } = 22;

        /// <summary>
        /// 字段与列对应关系
        /// </summary>
        public List<ColumnOption<T>> ColumnOptions { get; set; }

        /// <summary>
        /// 数据源
        /// </summary>
        public IEnumerable<T> DataSources { get; set; }

        /// <summary>
        /// 标题行样式
        /// </summary>
        protected ICellStyle TitleCellStyle { get; set; }

        /// <summary>
        /// 数据行样式
        /// </summary>
        protected ICellStyle DataCellStyle { get; set; }
        
        /// <summary>
        /// 是否写入到工作簿中
        /// </summary>
        public bool Written { get; set; }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="sheetName"></param>
        /// <param name="isAutoOrder"></param>
        public SheetMapper(ExcelMapper excelMapper, string sheetName)
        {
            this.ExcelMapper = excelMapper;
            this.Workbook = excelMapper.Workbook;
            this.SheetName = sheetName;
            ColumnOptions = new List<ColumnOption<T>>();
            SetDefaultTitleCellStyle(); //设置缺省的标题样式
            SetDefaultDataCellStyle(); //设置缺省的数据样式
        }

        /// <summary>
        /// 设置缺省的标题样式
        /// </summary>
        /// <returns></returns>
        private ICellStyle SetDefaultTitleCellStyle() {

            //标题行样式
            TitleCellStyle = Workbook.CreateCellStyle();
            IFont font = Workbook.CreateFont();
            font.FontHeightInPoints = 12;
            font.FontName = "宋体";
            font.IsBold = true;
            font.Color = IndexedColors.Green.Index;
            TitleCellStyle.SetFont(font);
            TitleCellStyle.Alignment = HorizontalAlignment.Center;
            TitleCellStyle.VerticalAlignment = VerticalAlignment.Center;
            TitleCellStyle.WrapText = true;
            //TitleCellStyle.BorderTop = BorderStyle.Thin;
            //TitleCellStyle.BorderLeft = BorderStyle.Thin;
            //TitleCellStyle.BorderRight = BorderStyle.Thin;
            //TitleCellStyle.BorderBottom = BorderStyle.Thin;
            return TitleCellStyle;
        }

        /// <summary>
        /// 设置缺省的数据样式
        /// </summary>
        /// <returns></returns>
        private ICellStyle SetDefaultDataCellStyle() {
            //数据行样式
            DataCellStyle = Workbook.CreateCellStyle();
            IFont dataFont = Workbook.CreateFont();
            dataFont.FontHeightInPoints = 11;
            dataFont.FontName = "宋体";
            DataCellStyle.SetFont(dataFont);
            DataCellStyle.Alignment = HorizontalAlignment.Center;
            DataCellStyle.VerticalAlignment = VerticalAlignment.Center;
            DataCellStyle.WrapText = true;
            //DataCellStyle.BorderTop = BorderStyle.Thin;
            //DataCellStyle.BorderLeft = BorderStyle.Thin;
            //DataCellStyle.BorderRight = BorderStyle.Thin;
            //DataCellStyle.BorderBottom = BorderStyle.Thin;
            return DataCellStyle;
        }

        /// <summary>
        /// 配置标题行样式
        /// </summary>
        /// <param name="cellStyleAction"></param>
       public SheetMapper<T> ConfigTitleCellStyle(Action<ICellStyle> cellStyleAction)
        {
            if (cellStyleAction == null) return this;
            if (Workbook == null) return this;
            if (TitleCellStyle == null) TitleCellStyle = Workbook.CreateCellStyle();
            cellStyleAction(TitleCellStyle);
            return this;
        }

        /// <summary>
        /// 配置数据行样式
        /// </summary>
        /// <param name="cellStyleAction"></param>
        public SheetMapper<T> ConfigDataCellStyle(Action<ICellStyle> cellStyleAction)
        {
            if (cellStyleAction == null) return this;
            if (Workbook == null) return this;
            if (TitleCellStyle == null) TitleCellStyle = Workbook.CreateCellStyle();
            cellStyleAction(TitleCellStyle);
            return this;
        }


        /// <summary>
        /// 增加字段与列名对应关系
        /// </summary>
        /// <param name="columnAction"></param>  
        public void Map(Action<ColumnOption<T>> columnAction)
        {
            var option = new ColumnOption<T>();
            if (columnAction != null) columnAction(option);
            ColumnOptions.Add(option);
        }

        /// <summary>
        /// 设置标题
        /// </summary>
        /// <returns></returns>
        public int SetTitlesAndDataRowIndex(out Dictionary<int, ColumnOption<T>> showedColumns)
        {
            if (Sheet == null) throw new Exception("暂时未创建Sheet，无法创建标题行");
            var maxRowIndex = 0;
            foreach (var item in ColumnOptions)
            {
                if (item.IsExistChildren()) maxRowIndex = 1; //获得标题的最大行号            
            }
            //新增标题行
            ColumnOptions = ColumnOptions.OrderBy(m => m.Order).ToList(); //排序
            List<IRow> rows = new List<IRow>();
            for (var index = 0; index <= maxRowIndex; index++)
            {
                rows.Add((IRow)Sheet.CreateRow(index));
            }
            var startColumnIndex = 0;
            showedColumns = new Dictionary<int, ColumnOption<T>>();
            var dataColumnIndex = 0;
            NPOI.SS.UserModel.ICell cell = null;
            foreach (var item in ColumnOptions)
            {
                int columnSpan = item.GetColumnSpan();
                for (var rowIndex = 0; rowIndex <= maxRowIndex; rowIndex++)
                {
                    for (var index = 0; index < columnSpan; index++)
                    {
                        cell = rows[rowIndex].CreateCell(startColumnIndex + index);
                        cell.CellStyle = TitleCellStyle;
                    }
                }
                cell = rows[0].GetCell(startColumnIndex);
                cell.SetCellValue(item.ColumnName ?? ""); //设置首行标题
                if (item.IsExistChildren())
                {
                    Sheet.AddMergedRegion(new CellRangeAddress(0, 0, startColumnIndex, startColumnIndex + columnSpan - 1)); //合并列与行 ：起始行 结束行，起始列，结束列
                    for (var index = 0; index < item.Children.Count; index++)
                    {
                        var tempIndex = startColumnIndex + index;
                        cell = rows[1].GetCell(tempIndex); //设置子标题
                        cell.SetCellValue(item.Children[index].ColumnName??"");
                        showedColumns.Add(tempIndex, item.Children[index]);
                    }
                }
                else
                {
                    if (maxRowIndex == 1) Sheet.AddMergedRegion(new CellRangeAddress(0, 1, startColumnIndex, startColumnIndex));
                    showedColumns.Add(startColumnIndex, item);
                }
                startColumnIndex += columnSpan;
            }
            //设置列宽
            foreach (var key in showedColumns.Keys) {
                Sheet.SetColumnWidth(key, showedColumns[key].ColumnWidth * 256); //设置列宽  
            }
            return maxRowIndex + 1;//返回数据的最大行号
        }

        /// <summary>
        /// 数据写入到工作簿中
        /// </summary>
        public void Write()
        {
            if (this.Written) return; //如果工作簿已写入文档，则不执行写入操作
            Sheet = Workbook.CreateSheet(this.SheetName);//创建工作簿
            Sheet.DefaultRowHeight = (short)(this.DefaultRowHeight *20);     
            Sheet.DisplayGridlines = true;

            this.Written = true;//标记文档已写入Excel
            Dictionary<int, ColumnOption<T>> showedColumns;
            var dataRowIndex = SetTitlesAndDataRowIndex(out showedColumns); //设置标题行
            var isExistGroup = showedColumns.Values.ToList<ColumnOption<T>>().Exists(m => m.ColumnType == ColumnType.MainGroupKey); //是否存在分组
            var groupColumnOption = showedColumns.Values.ToList<ColumnOption<T>>().FirstOrDefault(); //获得分组的列
            string groupKeyVal = string.Empty; //用于记录上次分组的值            
            var mainIndex = 1; //主序号
            var subIndex = 1; //子序号
            string val = string.Empty;
            int excelRowIndexToGroup = dataRowIndex; //起始数据行
            int excelRowIndex = dataRowIndex;
            int currentRowIndex = 0; //当前显示第几条数据
            foreach (T item in DataSources)
            {
                excelRowIndex = dataRowIndex + currentRowIndex; //在excel显示数据的行
                var row = Sheet.CreateRow(excelRowIndex); //在excel中创建行               
                //当存要分组时，并且当前显示的数据行与分组的起始行数据不相同时，启动合并
                if (isExistGroup && groupColumnOption != null && groupColumnOption.ColumnType == ColumnType.MainGroupKey)
                {
                    //当前列数据分组的主键列
                    val = GetRowCell(currentRowIndex, item, groupColumnOption);//currentRowIndex+1 表示当前数据源的序号
                    if (excelRowIndexToGroup == excelRowIndex) groupKeyVal = val; //首行时，赋值给分组的键值
                    else if (groupKeyVal != val)
                    {
                        if (excelRowIndexToGroup != (excelRowIndex - 1))
                        {
                            //分组的开始行与当前行的上一行比较，如果不是同一行，则合并
                            foreach (var key in showedColumns.Keys)
                            {
                                if (showedColumns[key].ColumnType != ColumnType.MainIndex && showedColumns[key].ColumnType != ColumnType.MainData) continue;
                                Sheet.AddMergedRegion(new CellRangeAddress(excelRowIndexToGroup, excelRowIndex - 1, key, key)); //合并列与行 ：起始行 结束行，起始列，结束列
                            }
                        }
                        excelRowIndexToGroup = excelRowIndex;//重新分组
                        subIndex = 1; //重新编号
                    }
                }
                foreach (var key in showedColumns.Keys)
                {
                    val = string.Empty;
                    var cell = (ICell)row.CreateCell(key); //创建单元格并设置值
                    cell.CellStyle = DataCellStyle; //设置数据列样式                                                           
                    var columnInfo = showedColumns[key];
                    if (columnInfo.ColumnType == ColumnType.MainIndex)
                    {
                        if (!isExistGroup || (isExistGroup && subIndex == 1)) cell.SetCellValue(mainIndex.ToString()); //设置主序列号                      
                        continue;
                    }
                    else if (columnInfo.ColumnType == ColumnType.MainData || columnInfo.ColumnType == ColumnType.MainGroupKey)
                    {
                        if (!isExistGroup || (isExistGroup && subIndex == 1))
                        {
                            val = GetRowCell(currentRowIndex, item, columnInfo);//currentRowIndex+1 表示当前数据源的序号     
                            cell.SetCellValue(val); //设置值
                        }
                        continue;
                    }
                    else if (columnInfo.ColumnType == ColumnType.SubIndex)
                    {
                        cell.SetCellValue(subIndex.ToString()); //设置子序列号
                    }
                    else if (columnInfo.ColumnType == ColumnType.SubData)
                    {
                        val = GetRowCell(currentRowIndex, item, columnInfo);//currentRowIndex+1 表示当前数据源的序号      
                        cell.SetCellValue(val);
                    }
                }
                if (!isExistGroup || (isExistGroup && subIndex == 1)) mainIndex++;//主序列累加1
                subIndex++;//子序列累加1
                currentRowIndex++;
            }
            if (isExistGroup && excelRowIndexToGroup != excelRowIndex && excelRowIndexToGroup < excelRowIndex)
            {
                foreach (var key in showedColumns.Keys)
                {
                    if (showedColumns[key].ColumnType != ColumnType.MainIndex && showedColumns[key].ColumnType != ColumnType.MainData 
                        && showedColumns[key].ColumnType != ColumnType.MainGroupKey) continue;
                    Sheet.AddMergedRegion(new CellRangeAddress(excelRowIndexToGroup, excelRowIndex, key, key)); //合并列与行 ：起始行 结束行，起始列，结束列
                }
            }
        }

        /// <summary>
        /// 数据容器只能放一个数据源
        /// </summary>
        /// <param name="data"></param>  
        public abstract void Put(object data);

        public abstract string GetRowCell(int rowIndex, T row, ColumnOption<T> columnOption);
    }

}
